﻿using System;
using System.Collections;
using Velocity4Net.Ctx;
using Velocity4Net.Errors;
using Velocity4Net.Runtime;
using Velocity4Net.Util;
using Velocity4Net.Util.Introspection;

namespace Velocity4Net.App.Event.Implement
{

    /*
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */












    /**
     * Use this event handler to flag invalid references.  Since this
     * is intended to be used for a specific request, this should be
     * used as a local event handler attached to a specific context
     * instead of being globally defined in the Velocity properties file.
     *
     * <p>
     * Note that InvalidReferenceHandler can be used
     * in two modes.  If the Velocity properties file contains the following:
     * <pre>
     * event_handler.invalid_references.exception = true
     * </pre>
     * then the event handler will throw a ParseErrorSystemException upon
     * hitting the first invalid reference.  This stops processing and is
     * passed through to the application code.  The ParseErrorSystemException
     * contain information about the template name, line number, column number,
     * and invalid reference.
     *
     * <p>
     * If this configuration setting is false or omitted then the page
     * will be processed as normal, but all invalid references will be collected
     * in a List of InvalidReferenceInfo objects.
     *
     * <p>This feature should be regarded as experimental.
     *
     * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
     * @version $Id: ReportInvalidReferences.java 1860691 2019-06-05 23:46:02Z cbrisson $
     * @since 1.5
     */
    public class ReportInvalidReferences :
        InvalidReferenceEventHandler, RuntimeServicesAware
    {
        public const String EVENTHANDLER_INVALIDREFERENCE_EXCEPTION = "event_handler.invalid_references.exception";

        [Obsolete]
        public const String OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION = "eventhandler.invalidreference.exception";

        /**
         * List of InvalidReferenceInfo objects
         */
        IList invalidReferences = new ArrayList();

        /**
         * If true, stop at the first invalid reference and throw an exception.
         */
        private bool stopOnFirstInvalidReference = false;


        /**
         * Collect the error and/or throw an exception, depending on configuration.
         *
         * @param context the context when the reference was found invalid
         * @param reference string with complete invalid reference
         * @param object the object referred to, or null if not found
         * @param property the property name from the reference
         * @param info contains template, line, column details
         * @return always returns null
         * @throws ParseErrorException
         */
        public Object invalidGetMethod(Context context, String reference, Object _object,
                String property, Info info)
        {
            reportInvalidReference(reference, info);
            return null;
        }

        /**
         * Collect the error and/or throw an exception, depending on configuration.
         *
         * @param context the context when the reference was found invalid
         * @param reference complete invalid reference
         * @param object the object referred to, or null if not found
         * @param method the property name from the reference
         * @param info contains template, line, column details
         * @return always returns null
         * @throws ParseErrorException
         */
        public Object invalidMethod(Context context, String reference, Object _object,
                String method, Info info)
        {
            if (reference == null)
            {
                reportInvalidReference(_object.GetType().Name + "." + method, info);
            }
            else
            {
                reportInvalidReference(reference, info);
            }
            return null;
        }

        /**
         * Collect the error and/or throw an exception, depending on configuration.
         *
         * @param context the context when the reference was found invalid
         * @param leftreference left reference being assigned to
         * @param rightreference invalid reference on the right
         * @param info contains info on template, line, col
         * @return loop to end -- always returns false
         */
        public bool invalidSetMethod(Context context, String leftreference, String rightreference, Info info)
        {
            reportInvalidReference(leftreference, info);
            return false;
        }


        /**
         * Check for an invalid reference and collect the error or throw an exception
         * (depending on configuration).
         *
         * @param reference the invalid reference
         * @param info line, column, template name
         */
        private void reportInvalidReference(String reference, Info info)
        {
            InvalidReferenceInfo invalidReferenceInfo = new InvalidReferenceInfo(reference, info);
            invalidReferences.Add(invalidReferenceInfo);

            if (stopOnFirstInvalidReference)
            {
                throw new ParseErrorException(
                        "Error in page - invalid reference.  ",
                        info,
                        invalidReferenceInfo.getInvalidReference());
            }
        }


        /**
         * All invalid references during the processing of this page.
         * @return a List of InvalidReferenceInfo objects
         */
        public IList getInvalidReferences()
        {
            return invalidReferences;
        }


        /**
         * Called automatically when event cartridge is initialized.
         * @param rs RuntimeServices object assigned during initialization
         */
        public void setRuntimeServices(RuntimeServices rs)
        {
            bool? b = rs.getConfiguration().getBoolean(OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, null);
            if (b == null)
            {
                b = rs.getConfiguration().getBoolean(EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, false);
            }
            else
            {
                rs.getLog().warn("configuration key '{}' has been deprecated in favor of '{}'", OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, EVENTHANDLER_INVALIDREFERENCE_EXCEPTION);
            }
            stopOnFirstInvalidReference = b.Value;
        }

    }
}